#include "in.h"

struct MemUndoData *GetUnusedUndoData( struct MemDatabase *db )
{
	struct MemDataPage	*undo_datapage = NULL ;
	struct MemUndoData	*undo_data = NULL ;
	
	undo_datapage = list_last_entry_or_null( & (db->undo_datapage_list) , struct MemDataPage , datapage_list_node ) ;
	TRACE_PRINTF( "undo_datapage[%p] .dataunit_used_count[%zu] .dataunit_prealloc_count[%zu]\n" , undo_datapage , undo_datapage?undo_datapage->dataunit_used_count:0 , undo_datapage?undo_datapage->dataunit_prealloc_count:0 );
	if( undo_datapage == NULL || IsDataPageFull(undo_datapage) )
	{
		undo_datapage = CreateDataPage( db , sizeof(struct MemUndoData) , & (db->undo_datapage_list) , & (db->undo_datapage_count) ) ;
		if( undo_datapage == NULL )
		{
			tls_last_errno = MDB_ERROR_ALLOC ;
			return NULL;
		}
		
		TRACE_PRINTF( "add undo_datapage[%p] to list , .dataunit_size[%zu]\n" , undo_datapage , undo_datapage->dataunit_size );
	}
	
	undo_data = (struct MemUndoData *)GetUnusedDataUnit( undo_datapage ) ;
	TRACE_PRINTF( "GetUnusedDataUnit undo_data[%p]\n" , undo_data );
	
	undo_data->undo_datapage = undo_datapage ;
	
	list_add_tail( & (undo_data->undo_list_node) , & (db->txn.undo_list) );
	
	return undo_data;
}

void SetUndoDataUnused( struct MemUndoData *undo_data )
{
	list_del( & (undo_data->undo_list_node) );
	
	if( undo_data->backup_record_dataunit )
	{
		TRACE_PRINTF( "SetUncommitRecordDataUnitUnused undo_data->backup_record_dataunit[%p]\n" , undo_data->backup_record_dataunit );
		SetUncommitRecordDataUnitUnused( undo_data->backup_record_dataunit );
	}
	
	if( undo_data->new_record_dataunit )
	{
		TRACE_PRINTF( "SetUncommitRecordDataUnitUnused undo_data->new_record_dataunit[%p]\n" , undo_data->new_record_dataunit );
		SetUncommitRecordDataUnitUnused( undo_data->new_record_dataunit );
	}
	
	TRACE_PRINTF( "SetDataUnitUnused undo_data[%p]\n" , undo_data );
	SetDataUnitUnused( undo_data->undo_datapage , (char*)undo_data );
	
	return;
}

int AppendInsertUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit )
{
	struct MemIndex			*index = NULL ;
	struct MemUndoData		*undo_data = NULL ;
	struct MemUndoData		*add_record_undo_data = NULL ;
	
	undo_data = GetUnusedUndoData( table->db ) ;
	undo_data->operation = MDB_UNDO_OPERATION_ADD_RECORD ;
	undo_data->table = table ;
	undo_data->new_record_dataunit = record_dataunit ;
	
	add_record_undo_data = undo_data ;
	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		undo_data = GetUnusedUndoData( table->db ) ;
		undo_data->operation = MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD ;
		undo_data->table = table ;
		undo_data->index = index ;
		undo_data->record_dataunit = NULL ;
		undo_data->undo_data = add_record_undo_data ;
	}
	
	return 0;
}

int AppendUpdateUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit , char *set_record )
{
	struct MemIndex			*index = NULL ;
	struct MemUndoData		*undo_data = NULL ;
	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		undo_data = GetUnusedUndoData( table->db ) ;
		undo_data->operation = MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD ;
		undo_data->table = table ;
		undo_data->record_dataunit = record_dataunit ;
		undo_data->index = index ;
	}
	
	undo_data = GetUnusedUndoData( table->db ) ;
	undo_data->operation = MDB_UNDO_OPERATION_BACKUP_AND_MODIFY_RECORD ;
	undo_data->table = table ;
	undo_data->record_dataunit = record_dataunit ;
	undo_data->backup_record_dataunit = GetUnusedUncommitRecordDataUnit( table ) ;
	undo_data->new_record_dataunit = GetUnusedUncommitRecordDataUnit( table ) ;
	
	memcpy( undo_data->backup_record_dataunit+sizeof(struct MemDataPage*) , undo_data->record_dataunit+sizeof(struct MemDataPage*) , sizeof(struct MemRecordDataUnitHead)+table->record_size-sizeof(struct MemDataPage*) );
	memcpy( undo_data->new_record_dataunit+sizeof(struct MemDataPage*) , undo_data->record_dataunit+sizeof(struct MemDataPage*) , sizeof(struct MemRecordDataUnitHead)-sizeof(struct MemDataPage*) );
	memcpy( MDB_RECORD_DATAUNIT_TO_DATA(undo_data->new_record_dataunit) , set_record , table->record_size );
	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		undo_data = GetUnusedUndoData( table->db ) ;
		undo_data->operation = MDB_UNDO_OPERATION_ADD_INDEX_ON_RECORD ;
		undo_data->table = table ;
		undo_data->record_dataunit = record_dataunit ;
		undo_data->index = index ;
	}
	
	return 0;
}

int AppendDeleteUndoDataToCurrentTransaction( struct MemTable *table , char *record_dataunit )
{
	struct MemDatabase		*db = table->db ;
	struct MemIndex			*index = NULL ;
	struct MemUndoData		*undo_data = NULL ;
	struct MemRecordDataUnitHead	*record_dataunit_head = NULL ;
	
	record_dataunit_head = MDB_RECORD_DATAUNIT_TO_HEAD(record_dataunit) ;
	record_dataunit_head->delete_txn_id = db->txn.txn_no ;
	
	list_for_each_entry( index , & (table->index_list) , struct MemIndex , index_list_node )
	{
		undo_data = GetUnusedUndoData( table->db ) ;
		undo_data->operation = MDB_UNDO_OPERATION_REMOVE_INDEX_ON_RECORD ;
		undo_data->table = table ;
		undo_data->record_dataunit = record_dataunit ;
		undo_data->index = index ;
	}
	
	undo_data = GetUnusedUndoData( table->db ) ;
	undo_data->operation = MDB_UNDO_OPERATION_BACKUP_AND_REMOVE_RECORD ;
	undo_data->table = table ;
	undo_data->record_dataunit = record_dataunit ;
	undo_data->backup_record_dataunit = GetUnusedUncommitRecordDataUnit( table ) ;
	memcpy( undo_data->backup_record_dataunit+sizeof(struct MemDataPage*) , undo_data->record_dataunit+sizeof(struct MemDataPage*) , sizeof(struct MemRecordDataUnitHead)+table->record_size-sizeof(struct MemDataPage*) );
	
	return 0;
}

